/*
 *      queryperf.c
 *      
 *      Copyright 2009 Daniele Ricci <denn86@gmail.com>, Luca Saccagi <luksak86@gmail.com>
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */
 
#ifndef _QUERYPERF_IMPL
#define _QUERYPERF_IMPL

#include "idmp.h"
/**\brief Carica in memoria un file contenente un alert in formato IDMEF e crea il relativo nodo XML
 *
 * Carica un file ed effettua i parsing del contenuto e la sua validazione in base al DTD dei messaggi IDMEF.
 * Crea il nodo XML da inserire nella ALertList relativo all'alert caricato dal file.
 * \param filename Nome del file da cui caricare l'alert
 * \param l Puntatore alla lista globale degli alert
 * \return Puntatore alla lista globale degli alert aggiornato dopo l'inserimento
 */
#ifdef _WIN32 
static AlertList *loadIdmefFile(const char *filename,AlertList *l){
#else
AlertList *loadIdmefFile(const char *filename,AlertList *l){
#endif
	FILE *fp1;
	char *buff,c='a';
	int size=0,bufflen=0,realloc_cnt=0,retry=0,cleaning=0;
	xmlDocPtr tmp_doc=NULL;
	xmlNodePtr tmp_xmlNode=NULL;
	
	for(retry=0,fp1=fopen(filename,"rt");((fp1==NULL) && (retry<=c0->read_retries));retry++,usleep(200),fp1=fopen(filename,"rt"));
	if(fp1==NULL){
		fprintf(stderr,"ERR - Cannot open %s file\n",filename);
		exit(-1);
	}

	while(!feof(fp1)){
		size=0;realloc_cnt=0,c='a';
		buff=NULL;
		buff=(char *)calloc(sizeof(char),c0->reallocation_size+1);
		for(cleaning=0;cleaning < c0->reallocation_size;cleaning++) buff[cleaning]='\0';
		realloc_cnt+=c0->reallocation_size;
		DBG_REALLOC_S ? printf("DBG - realloc_cnt(%d)\n",realloc_cnt) : 0;
		buff[size]=fgetc(fp1);
		while(buff[size]!='\n' && !feof(fp1)){
			/*DBG*/DBG_REALLOC_S ? printf("%c(%d/%d) _%s_\n",buff[size],size,realloc_cnt,buff)/*,getchar()*/ : 0;
			if(size == realloc_cnt){
				buff=(char*)realloc(buff,sizeof(char)*(size + c0->reallocation_size +1));
				realloc_cnt+=c0->reallocation_size;
				for(cleaning=size+1;cleaning <= realloc_cnt;cleaning++) buff[cleaning]='\0';
				
			}
			size++;
			buff[size]=fgetc(fp1);
		}
		buff[size]='\0';
		DBG_REALLOC_S ? printf("_%s_\n",buff) : 0;
		if(size>1){
			tmp_doc=xmlParseMemory((const char *)buff,size);
			DBG_REALLOC_S ? printf("dopo xmlParseMemory\n") : 0;
			tmp_xmlNode=xmlDocGetRootElement(tmp_doc);
			xmlUnlinkNode(tmp_xmlNode);
			xmlFreeDoc(tmp_doc);
			tmp_doc=NULL;
			l=AlertList_headInsert(l,tmp_xmlNode);
			setMessage(l,l->root);
			tmp_xmlNode=NULL;
		}
		free(buff);
		buff=NULL;
	}
	fclose(fp1);
	return l;
}
/**\brief Esegue la query ricevuta
 *
 * Riceve in input una query e ne effettua il parsing, dopodiché costruisce il relativo albero di esecuzione mediante
 * una in-visita (in modo da poter costruire un albero in cui l'operazione logica relatica ai risultati di due
 * filtri risulti essere il nodo padre di questi ultimi) e la lista delle stringhe della sezione \aselect.
 * Dopo aver costruito le strutture dati richiama la funzione execute che esegue l'albero.
 * \param cn Connessione sulla quale inviare i risultati dell'esecuzione della query
 * \param ch Canale sul quale inviare i risultati dell'esecuzione della query
 * \param query Stringa contenente la query ricevuta
 */
#ifdef _WIN32 
static void queryexec(VortexConnection *cn,VortexChannel *ch,char *query){
#else
void queryexec(VortexConnection *cn,VortexChannel *ch,char *query){
#endif
    int i=0,j=0,stop=0,slashfound=0,SelStrS=14,err_code;
    char *selStr;
	
	int selStr_size = 1;
	char sendingStr[11];
	char *realquery = NULL;
	stringList *sel=NULL,*cur=NULL;
	xmlNodeList *pos;
	processingTree *ptroot;
	int sign_num,step_num;
	unsigned int sign_mode;
	selStr=NULL;
	
	query[0] == '-' ? (sign_mode = 1,realquery=&query[1]) : (sign_mode = 0,realquery=&query[0]) ;
	sscanf(realquery,"%3d.%d",&sign_num,&step_num);
		
	if((strncasecmp(&realquery[7],"SELECT ",7)==0)&&((strstr(realquery,"where ")!=NULL)||(strstr(realquery,"WHERE ")!=NULL))){	
		i=SelStrS;
		while(i<strlen(realquery) && stop==0){
			if(i+1<strlen(realquery)){
				if((realquery[i]!=' ')&&((realquery[i]=='/')||(slashfound==1))){
					if(realquery[i]=='/' && realquery[i+1]==' '){	
						stop=1;
						error_manager(-450,cn,ch);
					}
					slashfound=1;
					if(selStr==NULL){
						selStr_size=1;
						selStr=(char *)calloc(sizeof(char),1);
					}else{
						selStr=(char *)realloc(selStr,selStr_size+1);
						selStr_size++;
					}
					selStr[j++]=realquery[i];
					i++;
				}else if(realquery[i]==' '){
					slashfound=0;
					selStr=(char *)realloc(selStr,selStr_size+1);
					selStr_size++;
					selStr[j]='\0';
					j=0;
					if((realquery[i+1]!='w')&&(realquery[i+1]!='W')){
						sel=stringList_headInsert(sel,selStr);
					}else{
						sel=stringList_headInsert(sel,selStr);
						stop=1;
					}
					i++;
					free(selStr);
					selStr=NULL;
				}
			}
		}
		if(strncasecmp(&realquery[i],"where ",6)==0){
			i+=6;
			ptroot=NULL;	
			ptroot=(processingTree *)malloc(sizeof(processingTree));
			ptroot->father=ptroot;
			ptroot->sign_mode=sign_mode;
			ptroot->sign_num=sign_num;
			ptroot->step_num=step_num;
			ptroot->sons=NULL;
			ptroot->prevbro=NULL;
			ptroot->nextbro=NULL;
			
			err_code=pTree_newnode(ptroot,&realquery[i],0,NEW_SON);
			err_code > 0 ? err_code = query_checks(err_code,strlen(&realquery[i]),&realquery[i]) : 0;
			/*DBG*/ DEBUG == DBG_TREE ? printTree(ptroot),getchar() : 0;


			if(err_code>0){
				sending=0;
				exists_sended=0;
				pos=execute(ptroot,sel,cn,ch);
				/*DBG*/ DEBUG == DBG_FRAMES ? printf("DBG - %010d\n",sending) : 0;
				snprintf(sendingStr,10,"%09d",sending);
				vortex_channel_send_ans_rpy(ch,sendingStr,10,vortex_channel_get_next_reply_no(ch));
				freexmlNodeList(pos);
				freestringList(sel);
				vortex_channel_finalize_ans_rpy(ch,vortex_channel_get_next_reply_no(ch));
				/*DBG*/ DEBUG == DBG_FRAMES ? printf("DBG - Answer finalized\n") : 0;
			}else{
				error_manager(err_code,cn,ch);
			}
			/* Parsing OK */
			if(DEBUG == DBG_SELECT){
				cur=sel;
				while(cur!=NULL){
					printf("DBG - %s\n",cur->str);
					cur=cur->next;
				}
			}
			freeTree(ptroot);
			error_manager(0,cn,ch);
		}else{
			error_manager(-450,cn,ch);
		}
	}else{
		error_manager(-450,cn,ch);
	}
}

#ifdef _WIN32 
static int query_checks(int i,int querylen, char *where){
#else
int query_checks(int i,int querylen, char *where){
#endif
	/*DBG*/DBG_TREE_PLUS ? printf(";\n",i,where[i]) : 0;
	if(i > querylen) return -502;
	if(parenthesis != 0) return -502;
	if(where[i] != WHERE_T) {/*DBG*/DBG_TREE_PLUS ? printf("(%d) %c != ;\n",i,where[i]) : 0 ; return -502;}
	return i;
}


/**\brief gestore degli errori
 *
 * Questa funzione si occupa di inviare l'opportuno messaggio di errore in base al codice di errore (RFC 3080).
 * \param error_code Codice di errore
 * \param cn Connessione sulla quale inviare il codice di errore
 * \param ch Canale sul quale inviare il codice di errore
 */
#ifdef _WIN32 
static void error_manager(int error_code,VortexConnection *cn,VortexChannel *ch){
#else
void error_manager(int error_code,VortexConnection *cn,VortexChannel *ch){
#endif
	switch(error_code){
		case 0:
			/*DBG*/DEBUG == DBG_ERRMAN ? printf("DBG - Operation Success\n") : 0;
			return;
		break;
		case -450:
			/*DBG*/DEBUG == DBG_ERRMAN ? printf("DBG - Syntax error\n") : 0;
			vortex_channel_send_rpy(ch,"<error code=\"450\"/>\0",20,vortex_channel_get_next_reply_no(ch));
			vortex_connection_close(cn);
		break;
		case -451:
			vortex_channel_send_rpy(ch,"<error code=\"451\">Unable to allocate memory</error>\0",52,vortex_channel_get_next_reply_no(ch));
		break;	
		case -500:
			vortex_channel_send_rpy(ch,"<error code=\"500\"/>\0",20,vortex_channel_get_next_reply_no(ch));
		break;
		case -501:
			vortex_channel_send_rpy(ch,"<error code=\"501\"/>\0",20,vortex_channel_get_next_reply_no(ch));
		break;
		case -502:
			vortex_channel_send_rpy(ch,"<error code=\"502\"/>\0",20,vortex_channel_get_next_reply_no(ch));
		break;
	};
	return;
}
/**\brief Esecuzione di un nodo dell'albero \aprocessingTree
 *
 * Questa funzione esegue in modo ricorsivo tutto l'albero di esecuzione attuando una previsita, in modo da eseguire
 * prima le operazioni dei nodi di livello più basso fino a risalire alla radice.
 * \param processingTree *this Puntatore al nodo corrente nell'albero di esecuzione
 * \param sel Puntatore alla lista di stringhe della sezione \aselect
 * \param cn Connessione sulla quale inviare i risultati dell'esecuzione della query
 * \param ch Canale sul quale inviare i risultati dell'esecuzione della query
 * \return xmlNodeList Puntatore alla lista dei risultati dell'operazione
 */
#ifdef _WIN32 
static xmlNodeList *execute(processingTree *this,stringList *sel,VortexConnection *cn,VortexChannel *ch){
#else
xmlNodeList *execute(processingTree *this,stringList *sel,VortexConnection *cn,VortexChannel *ch){
#endif
	xmlNodeList *lista=NULL,*listb=NULL;
	xmlNodeList *ret_list=NULL;
	processingTree *son=NULL;
	executing_func *localef;
	
	if(strncasecmp(this->OP,"AND",3)==0){
		son=this->sons;
		if(son!=NULL){
			lista=execute(son,NULL,NULL,NULL);
			son=son->nextbro;
			if(son!=NULL){
				listb=execute(son,NULL,NULL,NULL);
			}else{
				error_manager(-501,cn,ch);
				return NULL;
			}
		}
		return (this->father==this)?join(this->sign_mode,this->sign_num,this->step_num,lista,listb,sel,cn,ch):join(this->sign_mode,this->sign_num,this->step_num,lista,listb,NULL,NULL,NULL);
	}else if(strncasecmp(this->OP,"OR",2)==0){
		son=this->sons;
		if(son!=NULL){
			lista=execute(son,NULL,NULL,NULL);
			son=son->nextbro;
			if(son!=NULL){
				listb=execute(son,NULL,NULL,NULL);
			}else{
				error_manager(-501,cn,ch);
				return NULL;
			}
		}
		return (this->father==this)?or(this->sign_mode,this->sign_num,this->step_num,lista,listb,sel,cn,ch):or(this->sign_mode,this->sign_num,this->step_num,lista,listb,NULL,NULL,NULL);
	}else{
		for(localef=ef;localef;localef=localef->next){
			if(strncasecmp(this->OP,localef->op,2)==0){
				return (this->father==this)?localef->ex(this,sel,cn,ch):localef->ex(this,NULL,NULL,NULL);
			}
		}
	}
	return NULL;
}

#endif
